Skip to content

Add teemops.com as a design-partner recruitment page - #115

Open
kiwifellows wants to merge 1 commit into
developfrom
claude/email-mfa-roadmap-c99b1d
Open

Add teemops.com as a design-partner recruitment page#115
kiwifellows wants to merge 1 commit into
developfrom
claude/email-mfa-roadmap-c99b1d

Conversation

@kiwifellows

Copy link
Copy Markdown
Contributor

What and why

The design-partner milestone has been limited by recruitment, not engineering since 2026-08-01, and there was nowhere to send anyone — teemops.com served nothing. This adds a single page whose only job is turning a visitor into a design-partner conversation.

What was already in the repo was worse than nothing: design/marketing/web/ was a complete three-page site for a hosted commercial SaaS, with Free/Starter/Pro/Business pricing tiers and "Start Free" CTAs pointing at app.teem.nz/register — a domain that now redirects to an unrelated property. It contradicted D-1 and D-6 on its face and never mentioned that TOPS is open source. It is deleted, not archived: a second contradictory description of the product in the repo is a trap for whoever opens it next. Git history has it.

User story

Not a feature — this is a marketing site plus one endpoint, so no story in docs/features/. The reasoning is recorded in the roadmap as N-12 and D-14.

How it was tested

Automatednpm test in www/, 16 tests over POST /api/design-partner, run inside workerd against a real local D1 (@cloudflare/vitest-pool-workers), so the SQL genuinely executes rather than being mocked:

  • happy path, minimum-required-fields path, whitespace trimming and length caps
  • every validation rejection (missing name, malformed email, aws_scale outside the allowlist)
  • honeypot: drops the row, and is byte-for-byte indistinguishable from a genuine response
  • method guard (GET/PUT/DELETE → 405) and unknown-path 404

Every assertion checks the database as well as the status code, because the failure that matters is a request that answers 201 and stores nothing.

The suite was mutation-tested: disabling the honeypot fails a test, removing the aws_scale allowlist fails a test.

Manual — full submit through the real page JS into D1; contrast measured from live computed styles; layout checked at 1280px, 375px and 320px.

Claims on the page

Each was verified against the code, not against intent, and www/README.md records where:

Claim Source
74 checks basic.json (52) + cis.json (22)
11 services directories under app/rules/tasks/
all carry a remediation every rule has a non-empty remediation
28 critical/high have step-by-step guidance cross-checked against tips.json; none missing
v0.5.0, PCI empty, no telemetry VERSION, pci.json (0 rules), grep for analytics SDKs

Quality gate

  • Acceptance criteria are met
  • Solves the user problem simply — the decision framework was applied
  • Follows the practices in docs/practices/
  • Tests written and passing
  • Documentation updated
  • No obvious performance issues
  • Ready for production — the code is, but it is not deployed. Needs npm run db:init and teemops.com attached as a custom domain in the dashboard. No secrets.

Practices check

  • Multi-tenancy — not applicable. Nothing here touches tenant data; the D1 table is a marketing lead list, entirely outside the Laravel app.
  • Security — all input validated server-side and length-capped, aws_scale on an allowlist, SQL parameterised via .bind(), no secrets committed (there are none to commit), DB errors logged but never returned to the client. The endpoint is deliberately unauthenticated — it is a public signup form.
  • Simplicity — one page, no build step, no third-party scripts, no framework. Turnstile was removed rather than left gated behind a flag. No email notification until there is a lead to notify about.
  • Database — no Laravel migration. The D1 schema is one table, IF NOT EXISTS, applied by one command.
  • Scan definitions — untouched, so scan:validate-rules is not applicable. Worth noting it could not have been run in this worktree anyway: there is no vendor/, so php artisan does not boot.

Reviewer notes

  • www/ is a second Workers project. The repo root wrangler.jsonc is docs.teemops.com; this one is teemops.com. Run wrangler from inside www/.
  • worker-configuration.d.ts is gitignored. It is 14,707 generated lines; npm run typecheck regenerates it first. That is what took this diff from ~17.6k insertions to ~4.3k.
  • @cloudflare/vitest-pool-workers 0.20 changed its config API — the /config subpath and defineWorkersConfig are gone, and pool options now go to a cloudflareTest() plugin. Most examples online are still on the old shape; vitest.config.ts has a comment explaining it.

Out of scope

  • Deployment. Nothing is live. The dashboard steps are mine to write down, not to perform.
  • X-1 (new-device email OTP), which this branch was originally named for. Taken through Discovery and parked with no partner asking for it. Two findings kept in the roadmap: the "generator already exists, mostly extraction" claim is wrong, and the default install cannot send email to a real inbox at all — now filed as X-12, a live bug affecting email verification and organisation invitations today.
  • The Teemops/TOPS naming split. The page says "Teemops" per the brand guidelines; docs.teemops.com and user-docs/ say "TOPS". One of the two should move, and that decision is not made here.
  • The branch name (claude/email-mfa-roadmap-c99b1d) predates the pivot to this work and no longer describes it.

The Self-Hosted Design Partners milestone has been limited by recruitment
rather than engineering since 2026-08-01, and there was nowhere to send
anyone: teemops.com served nothing.

What was in the repo was worse than nothing. design/marketing/web/ held a
complete three-page site for a hosted commercial SaaS — Free/Starter/Pro/
Business pricing tiers, "Start Free" CTAs pointing at app.teem.nz/register,
a domain that now redirects to an unrelated property — and no mention that
TOPS is open source. It contradicted D-1 and D-6 on its face. It is deleted
rather than kept as reference, because a second contradictory description of
the product is a trap for whoever opens it next. Git history has it.

A brand-new page will not generate inbound leads inside the milestone window,
so this is built as a conversion asset for outbound rather than a lead engine:
one page, stating what TOPS is, proving it is real, saying plainly what it
does not do, and making one specific ask. The use-case, compliance and SEO
pages from the old content plan are dropped as premature at zero traffic.

Every number on the page is checked against the code rather than intent:
74 rules (52 basic + 22 CIS) across 11 services, all carrying a remediation,
all 28 critical/high carrying step-by-step guidance. www/README.md records
where each is verified.

Lead capture is a Worker writing to D1, protected by a honeypot field.
Turnstile was built, tested and removed before launch: at five-partner volume
a few junk rows are cheaper to skim than a widget is to run, the endpoint
sends no email and publishes nothing so spam has no amplification path, and a
third-party challenge script that fails to load makes the form unsubmittable
and loses a real lead silently. Bot Fight Mode and a rate-limiting rule are
the first two responses if spam arrives; Turnstile is the third.

Deployment reuses D-13's pattern (Workers static assets), so this adds no new
category of infrastructure — one CLI command, one dashboard step, no secrets.

Recorded as N-12 and D-14. The roadmap review that preceded this also found it
stale in four places, corrected here: N-11's checkboxes were unticked though it
shipped, the workstream's "Open — UI" table still listed four closed issues,
docs.teemops.com was recorded as not yet connected when it is live and serving,
and #109 and #111 appeared nowhere.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Ben Fellows <kiwifellows@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant